(PYTHON) Day - 23 Regex and Parsing(3)

Reference

  • 문제 출처 - HackerRank
  • 파이썬 연습 - Practice - Python

개인적인 생각과 상상으로 작성한 내용들이 포함되어 있습니다
문제를 풀고 Discussion Tab을 참고하며 코드 스타일을 개선하려고 노력하고자 합니다


HackerRank


Regex and Parsing


Hex Color Code


문제 : CSS 코드에서 HEX color code를 찾는 문제
입력 : 코드줄 수 N; (N 반복) CSS 코드
출력 : HEX color code

input
11
#BED
{
color: #FfFdF8; background-color:#aef;
font-size: 123px;
background: -webkit-linear-gradient(top, #f9f9f9, #fff);
}
#Cab
{
background-color: #ABC;
border: 2px dashed #fff;
}
output
#FfFdF8
#aef
#f9f9f9
#fff
#ABC
#fff
import re

for \_ in range(int(input())):
HEX = re.findall(r".(#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3})", input())
if HEX:
print(\*HEX, sep='\n')

HTML Parser - Part 1


문제 : html 문서를 태그별로 구분하는 문제

input
2

<html><head><title>HTML Parser - I</title></head>
<body data-modal-target class='1'><h1>HackerRank</h1><br /></body></html>
output
Start : html
Start : head
Start : title
End : title
End : head
Start : body
-> data-modal-target > None
-> class > 1
Start : h1
End : h1
Empty : br
End : body
End : html

end 태그 출력문에서 공백이 3개 있어야한다…

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
def handle_starttag(self, tag, attrs):
print("Start :", tag)
for e in attrs:
print ('->',e[0],'>',e[1])

def handle_endtag(self, tag):
print("End :", tag)

def handle_startendtag(self, tag, attrs):
print("Empty :", tag)
for e in attrs:
print ('->',e[0],'>',e[1])

parser = MyHTMLParser()
parser.feed(''.join([input() for _ in range(int(input()))]))

HTML Parser - Part 2


문제 : 주석과 데이터를 구분하는 문제

input
4

<!--[if IE 9]>IE9-specific content
<![endif]-->
<div> Welcome to HackerRank</div>
<!--[if IE 9]>IE9-specific content<![endif]-->
output

> > > Multi-line Comment
> > > [if IE 9]>IE9-specific content
> > > <![endif]
> > > Data
> > > Welcome to HackerRank
> > > Single-line Comment
> > > [if IE 9]>IE9-specific content<![endif]

handle_comment 와 handle_data 를 정의하는 문제

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
def handle_comment(self,data):
if '\n' in data:
print('>>> Multi-line Comment')
print(data)
else:
print('>>> Single-line Comment')
print(data)

def handle_data(self,data):
if data!='\n':
print('>>> Data')
print(data)

html = ""
for i in range(int(input())):
html += input().rstrip()
html += '\n'

parser = MyHTMLParser()
parser.feed(html)
parser.close()

Detect HTML Tags, Attributes and Attribute Values


문제 : 태그, 속성, 속성값을 구분하는 문제

input
9

<head>
<title>HTML</title>
</head>
<object type="application/x-flash"
data="your-file.swf"
width="0" height="0">
<!-- <param name="movie" value="your-file.swf" /> -->
<param name="quality" value="high"/>
</object>
output
head
title
object
-> type > application/x-flash
-> data > your-file.swf
-> width > 0
-> height > 0
param
-> name > quality
-> value > high
from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
def handle_starttag(self, tag, attrs):
print(tag)
[print('-> {} > {}'.format(*attr)) for attr in attrs]

html = '\n'.join([input() for _ in range(int(input()))])
parser = MyHTMLParser()
parser.feed(html)
parser.close()

정규식만 사용한 답안

import re

text = ''
for \_ in range(int(input())):
text = re.sub(r'<!.+-->',r' ',(text+input()))

for er in re.findall(r'<([^/][^>]_)>', text):
if ' ' in er:
for ere in re.findall(r'([a-z]+)? _([a-z-]+)="([^"]+)', er):
if ere[0]:
print(ere[0])
print('-> '+ere[1]+' > '+ere[2])
else:
print(er)

Validating UID


문제 : unique identification number(UID)를 구별하는 문제
조건 : 영숫자 범위(a-z, A-Z, 0-9) 사이에서 최소 2개의 대문자, 최소 3개의 숫자를 반복없이 포함해야하고 총 길이는 10이다
예제 : B1CD102354 에서는 1이 중복된다 -> Invalid

input
2
B1CD102354
B1CDEF2354
output
Invalid
Valid

매우 깔끔하고 좋은 것 같다. all() 함수의 사용에 익숙해지자.
특히 문자의 반복을 확인하는 정규표현식을 잘 기억하자
(?!패턴) : 부정적 뒤보기 선언. 이후에 나올 문자들이 ‘패턴’에 매치되어서는 안 된다.
참조 : \n : 예를 들어 작은 따옴표나 큰 따옴표 내에 한 개 이상의 문자가 있는 표현식인 r'['"][^'"]*['"]'가 있을 때, 시작하는 따옴표와 끝나는 따옴표가 똑같게 만들려면 r'(['"])[^'"]*\1' 이렇게 표현하면 된다. 여기서 \1은 첫번째 괄호인 ([‘“])이 매치되는 것과 같다.

import re

no_repeats = r"(?!._(.)._\1)"
two_or_more_upper = r"(._[A-Z]){2,}"
three_or_more_digits = r"(._\d){3,}"
ten_alphanumerics = r"[a-zA-Z0-9]{10}"
filters = no_repeats, two_or_more_upper, three_or_more_digits, ten_alphanumerics

for uid in [input() for _ in range(int(input()))]:
if all(re.match(f, uid) for f in filters):
print("Valid")
else:
print("Invalid")

assert 조건식은 처음봤는데 다음에 자세히 살펴봐야겠다. 우선 정렬시키고 조건을 확인하는 것이 인상적이였다

import re

for \_ in range(int(input())):
u = ''.join(sorted(input()))
try:
assert re.search(r'[A-Z]{2}', u)
assert re.search(r'\d\d\d', u)
assert not re.search(r'[^a-za-z0-9]', u)
assert not re.search(r'(.)\1', u)
assert len(u) == 10
except:
print('Invalid')
else:
print('Valid')

Validating Credit Card Numbers


문제 : 신용카드 번호의 유효성을 확인하는 문제
조건 :
4, 5, 6 중 하나로 시작해야한다
총 16자리이다
0-9 사이의 숫자로 이루어져있다
- 로 4자리씩 구분되어 있다
, _ 와 같은 다른 구분자를 사용해서는 안된다
숫자가 연속으로 4번 반복하면 안된다

input
6
4123456789123456
5123-4567-8912-3456
61234-567-8912-3456
4123356789123456
5133-3367-8912-3456
5123 - 3567 - 8912 - 3456
output
Valid # 4123456789123456
Valid # 5123-4567-8912-3456
Invalid # 61234-567-8912-3456 <- 567이 3자리이다
Valid # 4123356789123456
Invalid # 5133-3367-8912-3456 <- 33-33 반복이다
Invalid # 5123 - 3567 - 8912 - 3456 <- 구분자로 공백이 포함되어 있다
import re

def validate_credit_cards(credit_cards):
valid_structure = r"[456]\d{3}(-?\d{4}){3}$"
no_four_repeats = r"((\d)-?(?!(-?\2){3})){16}"
filters = valid_structure, no_four_repeats

if all(re.match(f, credit_cards) for f in filters):
print("Valid")
else:
print("Invalid")

for \_ in range(int(input())):
credit = input()
validate_credit_cards(credit)

정규식을 여러줄에 걸쳐서 작성할 수도 있음을 알게 되었다

import re
pattern = re.compile(r"^"
r"(?!.\*(\d)(-?\1){3})"
r"[456]"
r"\d{3}"
r"(?:-?\d{4}){3}"
r"$")
for \_ in range(int(input().strip())):
print("Valid" if pattern.search(input().strip()) else "Invalid")

Validating Postal Codes


문제 : 유효한 우편번호(postal code)를 확인하는 문제
조건 :
100000 - 999999 사이의 번호여야 한다
하나 이상의 alternating repetitive digit pair 가 없어야한다
(alternating repetitive digit란, 숫자 하나 건너 똑같은 숫자가 나오는 것을 말한다)
예제 :
523563 # 여기서는 alternating repetitive digit이 없다
552523 # 여기서는 숫자 2와 5가 alternating repetitive digits에 해당한다

110000

False

정규표현식의 시작과 끝을 정확히 명시해 줘야한다! ^ $를 명시하지 않으면 6자리를 넘어가는 번호도 유효하다고 처리한다

regex_integer_in_range = r"^[1-9]\d{5}$"
regex_alternating_repetitive_digit_pair = r"(\d)(?=\d\1)"

import re
P = input()

print (bool(re.match(regex_integer_in_range, P))
and len(re.findall(regex_alternating_repetitive_digit_pair, P)) < 2)

Matrix Script


문제 : 행렬로 암호가 주어졌을 때 이를 해독하는 문제
해독 방법 : 행으로 읽어가며 영문자만 가져온다
matrix

input
7 3
Tsi
h%x
i #
sM
$a
#t%
ir!
output
This is Matrix# %!

문자와 문자 사이에 특수문자가 있으면 ‘ ‘으로 치환하면 된다

import re

regex = re.compile(r'(?<=\w)(\W+)(?=\w)')
N, M = (int(num) for num in input().split())
encoded = [input() for _ in range(N)]
decoded = ''.join(row[letter] for letter in range(M) for row in encoded)
print(regex.sub(' ', decoded))